<script setup lang="ts">
import RoomStatus from './components/RoomStatus.vue'
import RoomAction from './components/RoomAction.vue'
import RoomMessage from './components/RoomMessage.vue'
import { io, Socket } from 'socket.io-client'
import { nextTick, onMounted, onUnmounted, provide, ref } from 'vue'
import { baseURL } from '@/utils/request'
import { useUserStore } from '@/stores'
import { useRoute } from 'vue-router'
import type { Message, TimeMessages } from '@/types/room'
import { MsgType, OrderType } from '@/enums'
import { reqGetConsultOrderDetail } from '@/services/consult'
import type { ConsultOrderItem, Image } from '@/types/consult'
import dayjs from 'dayjs'
import { Toast } from 'vant'

/*
chatMsgList 接收聊天记录

sendChatMsg 发送消息

receiveChatMsg 接收消息

updateMsgStatus 消息已读

getChatMsgList 获取聊天记录

statusChange 接收订单状态改变
*/

// 拿token
const store = useUserStore()
// 拿订单id orderid
const route = useRoute()
// 我们聊天用的渲染的消息列表，每条消息都是一条Message
const list = ref<Message[]>([])

// 准备时间
const time = ref(dayjs().format('YYYY-MM-DD HH:mm:ss'))
// 如果是第一次进入问诊室需要在最底部
const init = ref(true)

let socket: Socket
onMounted(() => {
  //第二个生命周期函数
  // 在onMounted中建立链接
  socket = io(baseURL, {
    auth: { token: `Bearer ${store.user?.token}` },
    query: { orderId: route.query.orderId }
  })
  // 建立链接
  socket.on('connect', () => {
    // console.log('connect')
    list.value = []
  })
  // 错误异常消息
  socket.on('error', () => {
    console.log('error')
  })
  // 已经断开连接
  socket.on('disconnect', () => {
    console.log('disconnect')
  })

  // 聊天记录 / 默认记录
  socket.on('chatMsgList', ({ data }: { data: TimeMessages[] }) => {
    const arr: Message[] = []
    data.forEach((item, index) => {
      // 获取第一条消息的时间
      if (index === 0) time.value = item.createTime
      // 处理一开始拼接的时间 普通消息
      arr.push({
        id: item.createTime,
        msgType: MsgType.Notify,
        createTime: item.createTime,
        msg: { content: item.createTime }
      })
      // 在把items加入
      arr.push(...item.items)
    })
    // 历史记录往前加
    list.value.unshift(...arr)
    // 下拉刷新之后数据加载了就关闭刷新
    loading.value = false

    // 如果下拉刷新获取到的数据是空数组 证明没有数据了，return 给提示
    if (!data.length) return Toast('没有聊天记录了')

    // 如果这个是true，则表示是第一次进入页面，
    if (init.value) {
      nextTick().then(() => {
        window.scrollTo(0, document.body.scrollHeight)
        // 进入之后就把init改为false 后面就不是第一次进入问诊室了，后面刷新就不算是第一次进入问诊室了
        init.value = false

        // 把最新的一条消息标记为已读，相当于把之前的消息都处理为已读
        socket.emit('updateMsgStatus', arr[arr.length - 1].id)
      })
    }
  })
  // 后端一旦改变了就会发给前端，前端再调用接口
  socket.on('statusChange', () => {
    loadDetailData()
  })

  // 发送消息 后端改状态改变后，会在发给我我们，我们再接收
  socket.on('receiveChatMsg', async (res) => {
    // 把这个push到list中去渲染
    list.value.push(res)
    // 发消息
    // nextTick().then(() => window.scrollTo(0, document.body.scrollHeight))
    await nextTick()
    window.scrollTo(0, document.body.scrollHeight)
    // 接受到的消息标记为已读
    socket.emit('updateMsgStatus', res.id)
  })
})

// 实现下拉刷新效果
// 记录每段消息的开始时间，作为下一次请求的开始时间
// 触发刷新，发送获取聊天记录消息
// 在接收聊天记录事件中
// 关闭刷新中效果
// 判断是否有数据？没有提示 没有聊天记录了
// 如果是初始化获取的聊天，需要滚动到最底部
// 如果是第二，三...次获取消息,不需要滚动到底部
// 如果断开连接后再次连接，需要清空聊天记录
const loading = ref(false)
const onRefresh = () => {
  console.log(loading.value)
  // 刷新后会自动更改loading问true，我们需要在获取到数据后，再把loading改为false
  socket.emit('getChatMsgList', 20, time.value, route.query.orderId)
}

// 组件卸载时清除socket
onUnmounted(() => {
  socket.close()
})

// 状态处理
// 1.类型定义
// 2.定义api函数
// 3.发送请求
// 3.1 一进页面调用请求，拿到详情数据
// 3.2 如果订单发生变化，我们通过socket.io 后端会告诉我们状态变更，此时在发送请求
// 4. 拿到我们订单状态，倒计时，父子通讯传给room status组件
// 5. 处理我们状态的逻辑
// 6. room-action 也有些状态需要控制，我们只有在咨询中才能发送文字和图片

// 定义一个页面需要渲染的内容对象，包括状态等等
const consult = ref<ConsultOrderItem>()
const loadDetailData = async () => {
  // 获取订单详情 订单状态
  const res = await reqGetConsultOrderDetail(route.query.orderId as string)
  consult.value = res.data
}
onMounted(() => {
  // 一进入页面就调用，如果医生接单了就会在下一个onMounted中的socket中再调用用这个方法，去更新数据重新渲染
  loadDetailData()
})

//  ==================================
//      底部组件聊天 发送文字和图片
// 底部操作组件，可以输入文字，双向数据绑定，触发 send-text 事件传出文字
// 问诊室组件，监听 send-text 事件接收文字
// 获取订单详情，需要使用医生ID，作为收信息人的标识
// 通过 socket.emit 的 sendChatMsg 发送文字给服务器
// 通过 socket.on 的 receiveChatMsg 接收发送成功或者医生发来的消息
// 展示消息

// 向服务端发送文字
const handAdopt = (text: string) => {
  socket.emit('sendChatMsg', {
    from: store.user?.id,
    to: consult.value?.docInfo?.id,
    msgType: MsgType.MsgText,
    msg: {
      content: text
    }
  })

  // 发送过去之后再在第二个onMounted 函数中用socket.on拿到数据，‘receiveChatMsg’ 接受
}

// 向服务端发送照片
const sendImgFn = (img: Image) => {
  socket.emit('sendChatMsg', {
    from: store.user?.id,
    to: consult.value?.docInfo?.id,
    msgType: MsgType.MsgImage,
    msg: {
      picture: img
    }
  })
}

// 孙子组件EvaluateCard 需要用 index中的 consult
// 完成评价后，修改list中 表单的type 改成 已完成评价结果
const completeEva = async (score: number) => {
  const item = list.value.find((item) => item.msgType === MsgType.CardEvaForm)
  if (item) {
    // 找到对应数据了在改对应的msgType score
    item.msgType = MsgType.CardEva
    // await nextTick()
    // // if (item.msg.evaluateDoc) {
    // item.msg.evaluateDoc!.score = score
    // // }

    item.msg.evaluateDoc = { score: score }
  }
}

provide('consult', consult)
provide('completeEva', completeEva)
</script>

<template>
  <div class="room-page">
    <!-- 顶部 -->
    <cp-nav-bar title="问诊室" @click-left="$router.push('/user/consult')" />

    <!-- 聊天框状态 -->
    <room-status :status="consult?.status" :countdown="consult?.countdown"></room-status>

    <!-- 消息卡片 -->
    <!-- 用下拉刷新组件包裹room-message -->
    <van-pull-refresh v-model="loading" @refresh="onRefresh">
      <room-message :list="list"></room-message>
    </van-pull-refresh>

    <!-- 底部输入          这里也需要通过拿到的状态控制底部输入框 -->
    <!-- 可选链不能被赋值，但可以判断，如果我们的状态（OrderType.ConsultChat表示问诊中） -->
    <!-- 如果我们的 -->
    <room-action
      :disabled="consult?.status === OrderType.ConsultChat"
      @send-text="handAdopt"
      @send-img="sendImgFn"
    ></room-action>
  </div>
</template>

<style lang="scss" scoped>
.room-page {
  padding-top: 90px;
  padding-bottom: 60px;
  min-height: 100vh;
  box-sizing: border-box;
  background-color: var(--cp-bg);
  .van-pull-refresh {
    width: 100%;
    min-height: calc(100vh - 150px);
  }
}
</style>
